home *** CD-ROM | disk | FTP | other *** search
/ Cream of the Crop 26 / Cream of the Crop 26.iso / os2 / plnk081.zip / pilot-link.0.8.1 / pd-tty.c < prev    next >
C/C++ Source or Header  |  1997-08-03  |  13KB  |  540 lines

  1. /* pd-tty.c: Text asynchronous input/output support for pilot-debug. 
  2.  *           Currently includes interfaces to STDIO (using plus-patch style
  3.  *           handlers), readline 2.0 (using hack of readline internals to
  4.  *           simulate co-routine), readline 2.1 (using callback mechanism)
  5.  *           and Tk (using a standard text widget.)
  6.  *
  7.  * This is free software, licensed under the GNU Public License V2.
  8.  * See the file COPYING for details.
  9.  * 
  10.  */
  11.  
  12. void display(char * text, char * tag, int type);
  13. void do_readline(void);
  14.  
  15. #include <stdio.h>
  16. #include <stdlib.h>
  17. #include <unistd.h>
  18. #include <signal.h>
  19. #include <sys/time.h>
  20. #include <sys/types.h>
  21. #include "pi-source.h"
  22. #include "pi-socket.h"
  23. #include "pi-dlp.h"
  24. #include "pi-syspkt.h"
  25.  
  26. #ifdef TK
  27. extern int usetk;
  28. # include <tk.h>
  29. #else
  30. # include <tcl.h>
  31. #endif
  32.  
  33. #ifndef TCL_ACTIVE
  34. # define TCL_ACTIVE TCL_READABLE
  35. #endif
  36.  
  37. extern int Interactive;
  38.  
  39. extern Tcl_Interp * interp;        
  40.  
  41. extern int tty;            /* Non-zero means standard input is a
  42.                  * terminal-like device.  Zero means it's*/
  43.  
  44. #if defined(READLINE_2_1)
  45.  
  46. #include <readline/readline.h>
  47. #include <readline/history.h>
  48.  
  49. static Tcl_DString command;
  50.  
  51. static int mode = 0;
  52.  
  53. static void Readable(ClientData d, int mask) {
  54.     mode = 1;
  55.     rl_callback_read_char();
  56. }
  57.  
  58.  
  59. static void Exit(ClientData d) {
  60.     rl_callback_handler_remove();
  61.     if (mode)
  62.         puts("");
  63. }
  64.  
  65.  
  66. static void execline(char * line)
  67. {
  68.    int gotPartial = 0;
  69.    char * cmd;
  70.  
  71.    if (line == 0) {
  72.       char buf[20];
  73.       int exitCode = 0;
  74.       sprintf(buf, "exit %d", exitCode);
  75.       Tcl_Eval(interp, buf);
  76.       return;
  77.    }
  78.    (void) Tcl_DStringAppend(&command, line, -1);
  79.    cmd = Tcl_DStringAppend(&command, "\n", -1);
  80.       
  81.    add_history(line);
  82.       
  83.    if (!Tcl_CommandComplete(cmd)) {
  84.       gotPartial = 1;
  85.    } else {
  86.       int code;
  87.       gotPartial = 0;
  88.       mode = 0;
  89.       code = Tcl_RecordAndEval(interp, cmd, TCL_EVAL_GLOBAL);
  90.          
  91.       Tcl_DStringFree(&command);
  92.       if (*interp->result != 0) {
  93.          Tcl_Channel chan;
  94.          if (code != TCL_OK) {
  95.             chan = Tcl_GetChannel(interp, "stderr", NULL);
  96.          } else {
  97.             chan = Tcl_GetChannel(interp, "stdout", NULL);
  98.          }
  99.          if (chan) {
  100.             Tcl_Write(chan, interp->result, -1);
  101.             Tcl_Write(chan, "\n", 1);
  102.          }
  103.       }
  104.       Tcl_ResetResult(interp);
  105.    }
  106.  
  107.    rl_callback_handler_install( gotPartial ? "> " : "pilot-debug> ", execline);
  108. }
  109.  
  110. void do_readline(void)
  111. {
  112.    char buf[20];
  113.    int exitCode = 0;
  114.    Tcl_Channel in = Tcl_GetStdChannel(TCL_STDIN);
  115.    
  116.    Tcl_SetChannelOption(interp, in, "-blocking", "off");
  117.    
  118.    Tcl_CreateChannelHandler(in, TCL_READABLE, Readable, 0);
  119.    
  120.    rl_callback_handler_install( "pilot-debug> ", execline );
  121.  
  122.    Tcl_CreateExitHandler((Tcl_ExitProc *) Exit, (ClientData) 0);
  123.  
  124.    while (Tcl_DoOneEvent(0)) {
  125.    }
  126.  
  127.    sprintf(buf, "exit %d", exitCode);
  128.    Tcl_Eval(interp, buf);
  129.    return;
  130.  
  131. }
  132.  
  133. #endif
  134. #if !defined(READLINE_2_1) && defined(READLINE_2_0)
  135.  
  136. #include <readline/readline.h>
  137. #include <readline/history.h>
  138.  
  139. /* Undocumented readline-2.0 internals */
  140. extern void rl_deprep_terminal(void);
  141. extern int rl_getc(FILE * stream);
  142. extern void rl_gather_tyi(void);
  143.  
  144. static volatile int readable = 0;
  145.  
  146. static Tcl_DString command;
  147.  
  148. static int mode = 0;
  149.  
  150. static void Readable(ClientData d, int mask) { readable = 1; }
  151.  
  152. static void Exit(ClientData d) {
  153.     rl_deprep_terminal(); 
  154. }
  155.  
  156. void do_readline(void)
  157. {
  158.    char buf[20];
  159.    int gotPartial = 0;
  160.    int exitCode = 0;
  161.    Tcl_Channel in = Tcl_GetStdChannel(TCL_STDIN);
  162.    
  163.    Tcl_SetChannelOption(interp, in, "-blocking", "off");
  164.    
  165.    Tcl_CreateChannelHandler(in, TCL_READABLE, Readable, 0);
  166.  
  167.    Tcl_CreateExitHandler((Tcl_ExitProc *) Exit, (ClientData) 0);
  168.    
  169.    for(;;) {
  170.       char * line = readline(gotPartial ? "> " : "pilot-debug> ");
  171.       char * cmd;
  172.       int code;
  173.       if (!line)
  174.          break;
  175.       (void) Tcl_DStringAppend(&command, line, -1);
  176.       cmd = Tcl_DStringAppend(&command, "\n", -1);
  177.       
  178.       add_history(line);
  179.       free(line);
  180.       
  181.       if (!Tcl_CommandComplete(cmd)) {
  182.          gotPartial = 1;
  183.       } else {
  184.          gotPartial = 0;
  185.          code = Tcl_RecordAndEval(interp, cmd, TCL_EVAL_GLOBAL);
  186.          
  187.          
  188.          Tcl_DStringFree(&command);
  189.          if (*interp->result != 0) {
  190.             Tcl_Channel chan;
  191.             if (code != TCL_OK) {
  192.                chan = Tcl_GetChannel(interp, "stderr", NULL);
  193.             } else {
  194.                chan = Tcl_GetChannel(interp, "stdout", NULL);
  195.             }
  196.             if (chan) {
  197.                Tcl_Write(chan, interp->result, -1);
  198.                Tcl_Write(chan, "\n", 1);
  199.             }
  200.          }
  201.       }
  202.       Tcl_ResetResult(interp);
  203.    }
  204.    sprintf(buf, "exit %d", exitCode);
  205.    Tcl_Eval(interp, buf);
  206. }
  207.  
  208.  
  209. /* Replace internal readline routine that retrieves a character */
  210. int rl_getc(FILE * stream)
  211. {
  212.    unsigned char c;
  213.    int d;
  214.    
  215.    for(;;) {
  216.       if (!readable)
  217.           Tcl_DoOneEvent(0);
  218.       d = Tcl_Read(Tcl_GetStdChannel(TCL_STDIN), &c, 1);
  219.       readable = 0;
  220.       if (d == 1) {
  221.          if (mode) {
  222.             printf("\n");
  223.             mode = 0;
  224.             rl_forced_update_display();
  225.          }
  226.          return (unsigned int)c;
  227.       }
  228.    }
  229. }
  230.  
  231. /* Replace internal readline routine that gets a character without blocking */
  232. void rl_gather_tyi(void)
  233. {
  234.    unsigned char c;
  235.    int d;
  236.    
  237.    Tcl_DoOneEvent(TCL_DONT_WAIT);
  238.    d = Tcl_Read(Tcl_GetStdChannel(TCL_STDIN), &c, 1);
  239.    if (d == 1) {
  240.       if (mode) {
  241.          printf("\n");
  242.          mode = 0;
  243.          rl_forced_update_display();
  244.       }
  245.       rl_stuff_char(c);
  246.    }
  247.    return;
  248. }
  249. #endif
  250. #if !defined(READLINE_2_1) && !defined(READLINE_2_0)
  251.  
  252. static void
  253. StdinProc(ClientData clientData, int mask);
  254.  
  255. static void
  256. Prompt(Tcl_Interp * interp, int partial);
  257.  
  258.  
  259. static Tcl_DString command;    /* Used to buffer incomplete commands being
  260.                  * read from stdin. */
  261. static Tcl_DString line;    /* Used to read the next line from the
  262.                                  * terminal input. */
  263.  
  264. static int gotPartial = 0;
  265.  
  266. static int mode = 0;
  267.  
  268.  
  269. void do_readline(void)
  270. {
  271.     char buf[20];
  272.     int exitCode = 0;
  273.     Tcl_Channel inChannel, outChannel;
  274.  
  275.     /*
  276.      * Process commands from stdin until there's an end-of-file.  Note
  277.      * that we need to fetch the standard channels again after every
  278.      * eval, since they may have been changed.
  279.      */
  280.  
  281.     inChannel = Tcl_GetChannel(interp, "stdin", NULL);
  282.     if (inChannel) {
  283.     Tcl_CreateChannelHandler(inChannel, TCL_READABLE|TCL_ACTIVE,
  284.         StdinProc, (ClientData) inChannel);
  285.     }
  286.     if (tty) {
  287.     Prompt(interp, 0);
  288.     }
  289.     outChannel = Tcl_GetChannel(interp, "stdout", NULL);
  290.     if (outChannel) {
  291.     Tcl_Flush(outChannel);
  292.     }
  293.     Tcl_DStringInit(&command);
  294.     Tcl_CreateExitHandler((Tcl_ExitProc *) Tcl_DStringFree, (ClientData) &command);
  295.     Tcl_DStringInit(&line);
  296.     Tcl_ResetResult(interp);
  297.  
  298.     /*
  299.      * Loop infinitely until all event handlers are passive. Then exit.
  300.      * Rather than calling exit, invoke the "exit" command so that
  301.      * users can replace "exit" with some other command to do additional
  302.      * cleanup on exit.  The Tcl_Eval call should never return.
  303.      */
  304.      
  305.     while (Tcl_DoOneEvent(0)) {
  306.     }
  307.     sprintf(buf, "exit %d", exitCode);
  308.     Tcl_Eval(interp, buf);
  309.     return;
  310. }
  311.  
  312. /*
  313.  *----------------------------------------------------------------------
  314.  *
  315.  * StdinProc --
  316.  *
  317.  *    This procedure is invoked by the event dispatcher whenever
  318.  *    standard input becomes readable.  It grabs the next line of
  319.  *    input characters, adds them to a command being assembled, and
  320.  *    executes the command if it's complete.
  321.  *
  322.  * Results:
  323.  *    None.
  324.  *
  325.  * Side effects:
  326.  *    Could be almost arbitrary, depending on the command that's
  327.  *    typed.
  328.  *
  329.  *----------------------------------------------------------------------
  330.  */
  331.  
  332.  
  333.     /* ARGSUSED */
  334. static void
  335. StdinProc(clientData, mask)
  336.     ClientData clientData;        /* Not used. */
  337.     int mask;                /* Not used. */
  338. {
  339.     char *cmd;
  340.     int code, count;
  341.     Tcl_Channel newchan, chan = (Tcl_Channel) clientData;
  342.  
  343.     count = Tcl_Gets(chan, &line);
  344.  
  345.     if (count < 0) {
  346.     if (!gotPartial) {
  347.         if (tty) {
  348.         Tcl_Exit(0);
  349.         } else {
  350.         Tcl_DeleteChannelHandler(chan, StdinProc, (ClientData) chan);
  351.         }
  352.         return;
  353.     } else {
  354.         count = 0;
  355.     }
  356.     }
  357.  
  358.     (void) Tcl_DStringAppend(&command, Tcl_DStringValue(&line), -1);
  359.     cmd = Tcl_DStringAppend(&command, "\n", -1);
  360.     Tcl_DStringFree(&line);
  361.     
  362.     if (!Tcl_CommandComplete(cmd)) {
  363.         gotPartial = 1;
  364.         goto prompt;
  365.     }
  366.     gotPartial = 0;
  367.  
  368.     /*
  369.      * Disable the stdin channel handler while evaluating the command;
  370.      * otherwise if the command re-enters the event loop we might
  371.      * process commands from stdin before the current command is
  372.      * finished.  Among other things, this will trash the text of the
  373.      * command being evaluated.
  374.      */
  375.  
  376.     Tcl_CreateChannelHandler(chan, TCL_ACTIVE, StdinProc, (ClientData) chan);
  377.     code = Tcl_RecordAndEval(interp, cmd, TCL_EVAL_GLOBAL);
  378.     newchan = Tcl_GetChannel(interp, "stdin", NULL);
  379.     if (chan != newchan) {
  380.     Tcl_DeleteChannelHandler(chan, StdinProc, (ClientData) chan);
  381.     }
  382.     if (newchan) {
  383.     Tcl_CreateChannelHandler(newchan, TCL_READABLE | TCL_ACTIVE,
  384.         StdinProc, (ClientData) newchan);
  385.     }
  386.     Tcl_DStringFree(&command);
  387.     if (*interp->result != 0) {
  388.     if (code != TCL_OK) {
  389.         chan = Tcl_GetChannel(interp, "stderr", NULL);
  390.     } else if (tty) {
  391.         chan = Tcl_GetChannel(interp, "stdout", NULL);
  392.     } else {
  393.         chan = NULL;
  394.     }
  395.     if (chan) {
  396.         Tcl_Write(chan, interp->result, -1);
  397.         Tcl_Write(chan, "\n", 1);
  398.     }
  399.     }
  400.  
  401.     /*
  402.      * Output a prompt.
  403.      */
  404.  
  405.     prompt:
  406.     if (tty) {
  407.     Prompt(interp, gotPartial);
  408.     }
  409.     Tcl_ResetResult(interp);
  410. }
  411.  
  412. /*
  413.  *----------------------------------------------------------------------
  414.  *
  415.  * Prompt --
  416.  *
  417.  *    Issue a prompt on standard output, or invoke a script
  418.  *    to issue the prompt.
  419.  *
  420.  * Results:
  421.  *    None.
  422.  *
  423.  * Side effects:
  424.  *    A prompt gets output, and a Tcl script may be evaluated
  425.  *    in interp.
  426.  *
  427.  *----------------------------------------------------------------------
  428.  */
  429.  
  430. static void
  431. Prompt(interp, partial)
  432.     Tcl_Interp *interp;            /* Interpreter to use for prompting. */
  433.     int partial;            /* Non-zero means there already
  434.                      * exists a partial command, so use
  435.                      * the secondary prompt. */
  436. {
  437.     char *promptCmd;
  438.     int code;
  439.     Tcl_Channel outChannel, errChannel;
  440.  
  441.     errChannel = Tcl_GetChannel(interp, "stderr", NULL);
  442.  
  443.     promptCmd = Tcl_GetVar(interp,
  444.     partial ? "tcl_prompt2" : "tcl_prompt1", TCL_GLOBAL_ONLY);
  445.     if (promptCmd == NULL) {
  446.     outChannel = Tcl_GetChannel(interp, "stdout", NULL);
  447. defaultPrompt:
  448.     if (!partial && outChannel) {
  449.             Tcl_Write(outChannel, "% ", 2);
  450.     }
  451.     } else {
  452.     code = Tcl_Eval(interp, promptCmd);
  453.     outChannel = Tcl_GetChannel(interp, "stdout", NULL);
  454.     if (code != TCL_OK) {
  455.         Tcl_AddErrorInfo(interp,
  456.             "\n    (script that generates prompt)");
  457.             /*
  458.              * We must check that errChannel is a real channel - it
  459.              * is possible that someone has transferred stderr out of
  460.              * this interpreter with "interp transfer".
  461.              */
  462.  
  463.         errChannel = Tcl_GetChannel(interp, "stdout", NULL);
  464.             if (errChannel != (Tcl_Channel) NULL) {
  465.                 Tcl_Write(errChannel, interp->result, -1);
  466.                 Tcl_Write(errChannel, "\n", 1);
  467.             }
  468.         goto defaultPrompt;
  469.     } else if (*interp->result && outChannel) {
  470.         Tcl_Write(outChannel, interp->result, strlen(interp->result));
  471.     }
  472.     }
  473.     if (outChannel) {
  474.         Tcl_Flush(outChannel);
  475.     }
  476. }
  477.  
  478.  
  479.  
  480. #endif /*!USE_READLINE_2_0 and 2_1*/
  481.  
  482. void display(char * text, char * tag, int type)
  483. {
  484.    int i;
  485.  
  486. #ifdef TK
  487.    if (usetk) {
  488.       Tcl_DString disp;
  489.       Tcl_DStringInit(&disp);
  490.       if (mode == 0) {          
  491.          Tcl_DStringAppend(&disp,".f.t mark set display {insert linestart};", -1);
  492.          mode = 1;
  493.       }
  494.       Tcl_DStringAppend(&disp, ".f.t insert display",-1);
  495.       Tcl_DStringAppendElement(&disp, text);
  496.       if (tag)
  497.          Tcl_DStringAppendElement(&disp, tag);
  498.       if (strlen(text) && (text[strlen(text)-1] == '\n')) {
  499.          mode = 0;
  500.       }
  501.       if (mode == 0)
  502.          Tcl_DStringAppend(&disp,";.f.t mark unset display", -1);
  503.       /*printf("Exec |%s|\n", Tcl_DStringValue(&disp));*/
  504.       Tcl_Eval(interp,Tcl_DStringValue(&disp));
  505.       /*puts(interp->result);*/
  506.       Tcl_DStringFree(&disp);
  507.       return;
  508.    }
  509. #endif   
  510.    type++;
  511.    
  512.    for (i=0;i<strlen(text);i++) {
  513.       if (mode == 0) {
  514.          /* At prompt */
  515.          /* Dumb hack to erase prompt */
  516.          printf("\r                                               \r");
  517.          mode = -1; /* Beginning of line */
  518.       }
  519.       if (mode != type) {
  520.          if (mode != -1)
  521.             printf("\n"); /* end current line */
  522.          printf("%s", tag);
  523.          mode = type;
  524.       }
  525.       printf("%c", text[i]);
  526.       if (text[i] == '\n') {
  527.         mode = 0; 
  528. #ifdef READLINE_2_1
  529.         rl_forced_update_display(); /* Bring the prompt back */
  530. #else       
  531. #ifdef READLINE_2_0
  532.         rl_forced_update_display(); /* Bring the prompt back */
  533. #else
  534.     Prompt(interp, gotPartial);
  535. #endif        
  536. #endif
  537.       }
  538.    }
  539. }
  540.